home *** CD-ROM | disk | FTP | other *** search
- /*
- File: GXEditLine.c
-
- Contains:
-
- Written by: Barton R. House
-
- Copyright: © 1993 by Apple Computer, Inc., All rights reserved.
-
- */
-
- #include "GXEdit.h"
- #include "GXEditDebug.h"
- #include "GXEditDoc.h"
- #include "GXEditLine.h"
- #include "GXEditNewRun.h"
- #include "GXEditStyle.h"
- #include "GXEditSelection.h"
- #include "GXEditError.h"
-
- #include "graphics routines.h"
- #include "math routines.h"
- #include "graphics libraries.h"
- #include "layout routines.h"
- #include "selection library.h"
-
- #define MIN(x,y) ((x) < (y) ? (x) : (y))
- #define MAX(x,y) ((x) > (y) ? (x) : (y))
-
- static void CalcLayout(DocPtr dp, ParaPtr pp, LinePtr lp);
- static void CalcOffsets(DocPtr dp, LinePtr lp, short runIndex);
- static void CalcHeight(DocPtr dp, LinePtr lp);
- static void DrawHighlight(DocPtr dp, gxShape layout, short startOffset, short endOffset);
- static void DrawCaret(DocPtr dp, gxShape layout, short offset);
- static short GetPreviousOffset(LinePtr lp, short lineOffset);
-
- /* insert text into the gxLine */
-
- void InsertDocLineText(DocPtr dp, LinePtr lp, short lineOffset, short numText)
- {
- #pragma unused(pp)
- if(lineOffset < 0 || lineOffset > lp->numText)
- gxEditPostError(dp, gx_edit_internal_fatal_error);
-
- lp->numText += numText;
- lp->dirty = true;
-
- }
-
-
- /* join the two lines disposing of the right gxLine data structures */
-
- void JoinDocLines(LinePtr left, LinePtr right)
- {
- #pragma unused(dp)
- #pragma unused(pp)
- left->numText += right->numText;
- left->dirty = true;
-
- if(right->layout != nil)
- GXDisposeShape(right->layout);
-
- }
-
- void SplitDocLine(DocPtr dp, LinePtr lp, short lineOffset, LinePtr newLine)
- {
- #pragma unused(pp)
- if(lineOffset < 0 || lineOffset > lp->numText)
- gxEditPostError(dp, gx_edit_internal_fatal_error);
-
- lp->dirty = true;
-
- newLine->dirty = true;
- newLine->layout = nil;
-
- newLine->numText = lp->numText - lineOffset;
- lp->numText = lineOffset;
-
- }
-
- /* the gxLine is our basic drawing unit */
-
- void DrawDocLine(DocPtr dp, ParaPtr pp, LinePtr lp)
- {
- Rect rect;
- long docOffset;
- long startOffset;
- long endOffset;
- gxPoint textPosition;
- gxRectangle aRect;
- gxShape clip;
- Boolean drawLine;
- Boolean drawSelection;
- Boolean clearSelection;
- SelPtr sp;
- Boolean startAtEnd;
- Boolean endAtStart;
- Boolean isCaret;
-
- drawLine = !(dp->drawn & pp->drawn & lp->drawn);
-
- /* see if this gxLine is selected and needs to be drawn */
-
- sp = &dp->selection;
-
- docOffset = pp->docOffset + lp->paraOffset;
- endOffset = docOffset + lp->numText;
-
- startAtEnd = (sp->startDocOffset == endOffset);
- endAtStart = (sp->endDocOffset == docOffset);
- isCaret = (sp->endDocOffset == sp->startDocOffset);
-
- if(docOffset <= sp->endDocOffset && endOffset >= sp->startDocOffset &&
- !(isCaret && ((sp->endOfLine && endAtStart) || (!sp->endOfLine && startAtEnd))) &&
- !(!isCaret && (endAtStart || startAtEnd))) {
-
- /* this gxLine falls in the selection range */
-
- if(docOffset > dp->selection.startDocOffset)
- startOffset = docOffset;
- else
- startOffset = dp->selection.startDocOffset;
-
- if(docOffset + lp->numText < dp->selection.endDocOffset)
- endOffset = docOffset + lp->numText;
- else
- endOffset = dp->selection.endDocOffset;
-
- startOffset -= docOffset;
- endOffset -= docOffset;
-
- if(lp->selected)
- if(lp->startOffset != startOffset || lp->endOffset != endOffset)
- drawSelection = true;
- else
- drawSelection = false;
- else
- drawSelection = true;
-
- drawSelection |= drawLine;
-
- clearSelection = false;
-
- } else {
-
- if(lp->selected && !drawLine)
- clearSelection = true;
- else
- clearSelection = false;
-
- drawSelection = false;
-
- }
-
- if(!(drawLine || drawSelection || clearSelection))
- return;
-
- if(lp->dirty)
- CalcLayout(dp, pp, lp);
-
- /* set the correct clip */
-
- if(dp->verticalText) {
-
- rect.right = dp->viewRect.right - (pp->start + lp->start - dp->top);
- rect.left = rect.right - lp->height;
-
- rect.top = dp->viewRect.top;
-
- if(rect.left < dp->viewRect.left)
- rect.left = dp->viewRect.left;
-
- rect.bottom = dp->viewRect.bottom;
-
- if(rect.right > dp->viewRect.right)
- rect.right = dp->viewRect.right;
-
- } else {
-
- rect.top = dp->viewRect.top + pp->start + lp->start - dp->top;
- rect.bottom = rect.top + lp->height;
-
- if(rect.top < dp->viewRect.top)
- rect.top = dp->viewRect.top;
-
- rect.left = dp->viewRect.left;
-
- if(rect.bottom > dp->viewRect.bottom)
- rect.bottom = dp->viewRect.bottom;
-
- rect.right = dp->viewRect.right;
-
- }
-
- aRect.top = ff(rect.top);
- aRect.left = ff(rect.left);
- aRect.bottom = ff(rect.bottom);
- aRect.right = ff(rect.right);
-
- clip = GXNewRectangle(&aRect);
-
- /* This ignore is needed because we do not keep track of the current
- clip of the view port. Thus, we will often set the clip to the same rect
- which generates a notice*/
-
- #ifdef debugging
- GXIgnoreGraphicsNotice(clip_already_set);
- #endif
-
- GXSetViewPortClip(dp->docViewPort, clip);
-
- #ifdef debugging
- GXPopGraphicsNotice();
- #endif
-
- /* make sure the layout is at the right position */
-
- if(dp->verticalText) {
- textPosition.x = ff(dp->viewRect.right - (pp->start + lp->start - dp->top + lp->ascent));
- textPosition.y = ff(dp->viewRect.top + dp->leftMargin);
- } else {
- textPosition.x = ff(dp->viewRect.left + dp->leftMargin);
- textPosition.y = ff(dp->viewRect.top + pp->start + lp->start - dp->top + lp->ascent);
- }
-
- if(textPosition.x != lp->layoutPos.x || textPosition.y != lp->layoutPos.y) {
- GXMoveShapeTo(lp->layout, textPosition.x, textPosition.y);
- lp->layoutPos = textPosition;
- }
-
- if(drawLine) {
-
-
- /* erase the gxLine */
-
- SetShapeCommonColor(clip, gxWhite);
- GXSetShapeViewPorts(clip, 1, &dp->offscreenPort);
- GXDrawShape(clip);
-
- if(lp->numText == 0)
- return; /* nothing to draw or select */
-
- /* now draw the layout */
-
- GXDrawShape(lp->layout);
-
- /* now draw the gxBitmap to the screen */
-
- GXSetShapeViewPorts(dp->offscreenBitmap, 1, &dp->docViewPort);
- GXDrawShape(dp->offscreenBitmap);
- GXSetShapeViewPorts(dp->offscreenBitmap, 1, &dp->offscreenPort);
-
- lp->drawn = true;
- lp->selected = false;
-
- /* check to see if we drawn in the bottom cleared area */
-
- if(dp->verticalText) {
- if(dp->bottomClear < rect.left)
- dp->bottomClear = rect.left;
- } else{
- if(dp->bottomClear < rect.bottom)
- dp->bottomClear = rect.bottom;
- }
-
- }
-
- if(drawSelection) {
-
- if(lp->selected && (lp->startOffset == lp->endOffset)) {
-
- DrawCaret(dp, lp->layout, lp->startOffset);
- lp->selected = false;
-
- }
-
- if(startOffset == endOffset) {
-
- if(lp->selected)
- DrawHighlight(dp, lp->layout, lp->startOffset, lp->endOffset);
-
- DrawCaret(dp, lp->layout, startOffset);
-
-
- } else {
-
- if(lp->selected) {
-
-
- /* |--------------| */
- /* |----| case 0 */
- /* |---| case 1 */
- /* |-----| case 2 */
- /* |-----| case 3 */
- /* |----| case 4 */
- /* |----------------------| case 5 */
-
-
- if(startOffset < lp->startOffset) {
-
- if(endOffset > lp->endOffset) {
-
- /* case 5 */
-
- DrawHighlight(dp, lp->layout, startOffset, lp->startOffset);
- DrawHighlight(dp, lp->layout, lp->endOffset, endOffset);
-
- } else if(endOffset > lp->startOffset) {
-
- /* case 2 */
-
- DrawHighlight(dp, lp->layout, startOffset, lp->startOffset);
- DrawHighlight(dp, lp->layout, endOffset, lp->endOffset);
-
- } else {
-
- /* case 0 */
-
- DrawHighlight(dp, lp->layout, lp->startOffset, lp->endOffset);
- DrawHighlight(dp, lp->layout, startOffset, endOffset);
-
- }
-
- } else {
-
- if(startOffset > lp->endOffset) {
-
- /* case 1 */
-
- DrawHighlight(dp, lp->layout, lp->startOffset, lp->endOffset);
- DrawHighlight(dp, lp->layout, startOffset, lp->endOffset);
-
- } else if(endOffset < lp->endOffset) {
-
- /* case 4 */
-
- DrawHighlight(dp, lp->layout, lp->startOffset, startOffset);
- DrawHighlight(dp, lp->layout, endOffset, lp->endOffset);
-
- } else {
-
- /* case 3 */
-
- DrawHighlight(dp, lp->layout, lp->startOffset, startOffset);
- DrawHighlight(dp, lp->layout, lp->endOffset, endOffset);
- }
-
- }
-
- } else
- DrawHighlight(dp, lp->layout, startOffset, endOffset);
-
- }
-
- lp->selected = true;
- lp->startOffset = startOffset;
- lp->endOffset = endOffset;
-
- } else if(clearSelection) {
-
- if(lp->startOffset == lp->endOffset)
- DrawCaret(dp, lp->layout, lp->startOffset);
- else
- DrawHighlight(dp, lp->layout, lp->startOffset, lp->endOffset);
-
- lp->selected = false;
- }
-
- GXDisposeShape(clip);
-
- }
-
- void NewDocLine(LinePtr lp)
- {
- #pragma unused(dp)
- #pragma unused(pp)
-
- lp->layout = nil;
- lp->paraOffset = 0;
- lp->numText = 0;
- lp->start = 0;
- lp->reflow = false;
- lp->drawn = false;
- lp->selected = false;
- lp->dirty = true;
-
- }
-
- short HitTestDocLine(DocPtr dp, ParaPtr pp, LinePtr lp, Point where)
- {
- short lineOffset;
- gxLayoutHitInfo hitInfo;
- gxPoint hitDown;
-
- if(dp->verticalText) {
- if(where.v <= 0)
- return(0);
- } else {
- if(where.h <= 0)
- return(0);
- }
-
- if(lp->dirty)
- CalcLayout(dp, pp, lp);
-
- hitDown.x = ff(where.h) + lp->layoutPos.x;
- hitDown.y = ff(where.v) + lp->layoutPos.y;
-
- lineOffset = GXHitTestLayout((gxShape) lp->layout, &hitDown, gxHighlightStraight, &hitInfo, nil);
-
- return(lineOffset);
-
- }
-
- void DocLinePosition(LinePtr lp, long * start, long * end)
- {
- /* currently not fully supported */
- #pragma unused(lineOffset)
-
- *start = 0;
- *end = lp->height;
- }
-
- short GetLineOffset(DocPtr dp, ParaPtr pp, LinePtr lp, short offsetType, short lineOffset)
- {
- if(lp->dirty)
- CalcLayout(dp, pp, lp);
-
- switch(offsetType) {
- case kPreviousOffset:
- return(GetPreviousOffset(lp, lineOffset));
- case kVisualRightOffset:
- return(GXGetRightVisualOffset(lp->layout, lineOffset));
- case kVisualLeftOffset:
- return(GXGetLeftVisualOffset(lp->layout, lineOffset));
- case kDownOffset:
- case kUpOffset:
- default:
- /* ??? */
- gxEditPostError(dp, gx_edit_internal_fatal_error);
- break;
- }
-
- }
-
- static short GetPreviousOffset(LinePtr lp, short lineOffset)
- {
- unsigned short firstGlyph, secondGlyph;
- gxLayoutOffsetState offsetState;
- static SelectionOffset offsetStateSizes[] = {1, 1, 2, 2, 0};
-
- GXGetOffsetGlyphs(lp->layout, lineOffset, 0, &offsetState, &firstGlyph, &secondGlyph);
-
- return(lineOffset - offsetStateSizes[offsetState & ~gxOffsetInsideLigature]);
- }
-
- void DocLineClear(DocPtr dp, LinePtr lp, short lineOffset, short numText)
- {
- #pragma unused(pp)
-
- if(numText == 0)
- return;
-
- if(lineOffset < 0 || lineOffset > lp->numText)
- gxEditPostError(dp, gx_edit_internal_fatal_error);
-
- if((lineOffset + numText) > lp->numText)
- gxEditPostError(dp, gx_edit_internal_fatal_error);
-
- lp->numText -= numText;
- lp->dirty = true;
-
- }
-
- void DisposeDocLine(LinePtr lp)
- {
-
- if(lp->layout)
- GXDisposeShape(lp->layout);
-
- }
-
- short GetLineHeight(DocPtr dp, ParaPtr pp, LinePtr lp)
- {
- if(lp->dirty)
- CalcLayout(dp, pp, lp);
-
- return(lp->height);
- }
-
- static void CalcLayout(DocPtr dp, ParaPtr pp, LinePtr lp)
- {
- long textRunCount;
- short * textRunLengths;
- void ** textPtrs;
- long styleRunCount;
- short * styleRunLengths;
- gxStyle * styles;
- NewRunPtr rp;
- short runIndex;
- gxLayoutOptions options;
- short lineText;
- short runText;
- short maxRunCount;
- short startRunIndex;
- short runOffset;
- short i;
- StylePtr sp;
- char spaceChar = ' ';
- Boolean spaceSubstitution = false;
-
- if(lp->numText == 0) {
-
- /* really should create an empty gxShape and set gxLine values accordingly */
- /* but for speed -- we will just return for now */
- /* this should not be a problem */
-
- return;
- }
-
- HLock((Handle) pp->runs);
-
- if(lp->layout != nil)
- GXDisposeShape(lp->layout);
-
- maxRunCount = pp->numRuns + 1; /* +1 incase we need to stuff in a space in place of '\r' */
-
- textRunLengths = (short *) NewPtr(maxRunCount * sizeof(short));
- textPtrs = (void **) NewPtr(maxRunCount * sizeof(void *));
- styles = (gxStyle *) NewPtr(maxRunCount * sizeof(gxStyle));
-
- GetNewRunIndexAndOffset(dp, pp, lp->paraOffset, &startRunIndex, &runOffset);
- lineText = lp->numText;
- textRunCount = 0;
-
- runIndex = startRunIndex;
- rp = *pp->runs + runIndex;
- for(; runIndex < (maxRunCount - 1) && lineText > 0; runIndex++, rp++, lineText -= runText) {
-
- runText = (rp->numText - runOffset > lineText ? lineText : rp->numText - runOffset);
- textRunLengths[textRunCount] = runText;
-
- HLock((Handle) rp->text);
-
- if(textRunLengths[textRunCount] == 0) {
- gxEditPostError(dp, gx_edit_warning);
- continue;
- }
-
- sp = GetDocStyle(dp, rp->styleIndex);
-
- styles[textRunCount] = sp->textStyle;
-
- textPtrs[textRunCount] = (Ptr) *rp->text + runOffset;
-
- if(runIndex == (pp->numRuns - 1) && runText == (rp->numText - runOffset)) {
-
- /* end of paragraph -- should always be '\r' and never gxGlyphPlatform platform type */
-
- if(sp->platform == gxGlyphPlatform)
- gxEditPostError(dp, gx_edit_internal_fatal_error);
-
- if((((char **) textPtrs)[textRunCount])[runText-1] != '\r')
- gxEditPostError(dp, gx_edit_internal_fatal_error);
-
- textRunLengths[textRunCount] -= sizeof(char);
-
- /* don't count this run if no text in it -- GX can't handle empty style runs */
-
- if(textRunLengths[textRunCount] != 0)
- textRunCount++;
- else
- HUnlock((Handle) rp->text);
-
- textRunLengths[textRunCount] = sizeof(char);
- textPtrs[textRunCount] = (Ptr) &spaceChar;
-
- styles[textRunCount] = sp->textStyle;
-
- spaceSubstitution = true;
- }
-
- textRunCount++;
- runOffset = 0;
-
- }
-
- if(lineText > 0)
- gxEditPostError(dp, gx_edit_internal_fatal_error);
-
- styleRunCount = textRunCount;
- styleRunLengths = textRunLengths;
-
- /* setup layout options */
-
- options = pp->layoutOptions;
-
- /* turn off justification if last gxLine in paragraph */
-
- if(options.just != gxNoJustification &&
- StripAddress(lp) == StripAddress((*pp->lines + pp->numLines - 1)))
- options.just = gxNoJustification;
-
- /* these values may be totally wrong, but they might be right. If they are right, it
- will save us from having to move it later. And thus, save us from throwing away caches */
-
- if(dp->verticalText) {
- lp->layoutPos.x = ff(dp->viewRect.right - (pp->start + lp->start - dp->top + lp->ascent));
- lp->layoutPos.y = ff(dp->viewRect.top + dp->leftMargin);
- } else {
- lp->layoutPos.x = ff(dp->viewRect.left + dp->leftMargin);
- lp->layoutPos.y = ff(dp->viewRect.top + pp->start + lp->start - dp->top + lp->ascent);
- }
-
-
- if (textRunCount)
- lp->layout = (void *) GXNewLayout(textRunCount, textRunLengths, (void *) textPtrs,
- styleRunCount, styleRunLengths, styles, 0, nil, nil,
- &options, nil);
- else
- lp->layout = (void *) GXNewLayout(0, nil, nil, 0, nil, nil, 0, nil, nil, &options, nil);
-
- GXSetShapeViewPorts(lp->layout, 1, &dp->offscreenPort);
-
- GXMoveShapeTo(lp->layout, lp->layoutPos.x, lp->layoutPos.y);
-
- if(dp->verticalText)
- GXRotateShape(lp->layout, ff(90), lp->layoutPos.x, lp->layoutPos.y);
-
- if(spaceSubstitution)
- textRunCount--;
-
- rp = *pp->runs + startRunIndex;
-
- for(i = 0; i < textRunCount; i++, rp++)
- HUnlock((Handle) rp->text);
-
- DisposePtr((Ptr) textRunLengths);
- DisposePtr((Ptr) textPtrs);
- DisposePtr((Ptr) styles);
-
- CalcHeight(dp, lp);
-
- lp->drawn = false; /* this gxLine needs be redrawn */
- lp->dirty = false; /* no longer dirty */
-
- HUnlock((Handle) pp->runs);
- }
-
- #define adjustLineHeightForTallAndLowGlyphs /* undefine this to show the 'true' ascent and descent */
-
- static void CalcHeight(DocPtr dp, LinePtr lp)
- {
- fixed lineAscent;
- fixed lineDescent;
-
- if(lp->numText == 0) {
-
- lp->ascent = lp->descent = lp->height = 0;
- return;
-
- }
-
- GXGetLayoutSpan((gxShape) lp->layout, &lineAscent, &lineDescent);
-
- #ifdef adjustLineHeightForTallAndLowGlyphs
- {
- fixed boundsAscent, boundsDescent;
- gxRectangle bounds;
-
- GXGetShapeLocalBounds((gxShape) lp->layout, &bounds);
- boundsAscent = lp->layoutPos.y - bounds.top;
- boundsDescent = bounds.bottom - lp->layoutPos.y;
-
- /* the test for verticalText is temporary */
-
- if(!dp->verticalText && (boundsAscent > lineAscent || boundsDescent > lineDescent)) {
-
- lineAscent = lineAscent > boundsAscent ? lineAscent : boundsAscent;
- lineDescent = lineDescent > boundsDescent ? lineDescent : boundsDescent;
-
- GXSetLayoutSpan(lp->layout, lineAscent, lineDescent);
- }
- }
- #endif
-
- lp->ascent = FixedToInt(lineAscent);
- lp->descent = FixedToInt(lineDescent);
-
- lp->height = lp->ascent + lp->descent;
-
- }
-
- static void DrawHighlight(DocPtr dp, gxShape layout, short startOffset, short endOffset)
- {
- gxShape highlight;
-
- if(startOffset == endOffset)
- return; /* no highlight */
-
- highlight = GXGetLayoutHighlight(layout, startOffset, endOffset, gxHighlightAverageAngle, nil);
-
- SetShapeCommonTransfer(highlight, gxHighlightMode);
- SetShapeCommonColor(highlight, gxWhite);
- GXSetShapeViewPorts(highlight, 1, &dp->docViewPort);
-
- GXDrawShape(highlight);
-
- GXDisposeShape(highlight);
-
- }
-
- static void DrawCaret(DocPtr dp, gxShape layout, short offset)
- {
- gxShape caret;
-
- caret = GXGetLayoutCaret(layout, offset, gxHighlightAverageAngle, gxSplitCaretType, nil);
-
- SetShapeCommonTransfer(caret, gxXorMode);
- SetShapeCommonColor(caret, gxWhite);
- GXSetShapeViewPorts(caret, 1, &dp->docViewPort);
-
- GXDrawShape(caret);
-
- GXDisposeShape(caret);
-
- }
-
-